import hashlib
import numpy as np
from typing import List, Dict, Tuple

# --- 新增：哈希分桶参数 ---
# FUZ_FACTOR (Fuzz Factor) 用于降低时间和频率的精度，以提高鲁棒性。
# 这是一个关键参数，用于平衡精度和对噪声/压缩的抵抗能力。
# The FUZ_FACTOR is used to reduce the precision of time and frequency
# to improve robustness. This is a key parameter for balancing
# precision against resistance to noise/compression.
FUZ_FACTOR = 2

class Hasher:
    """
    负责将峰值点（星座图）转换为组合哈希指纹。
    Responsible for converting peaks (constellation map) into combinatorial hash fingerprints.
    """
    def __init__(self, config: Dict):
        """
        初始化哈希器。
        Initializes the Hasher.

        Args:
            config (Dict): 从 config.yaml 加载的哈希部分配置。
                           The hashing section of the configuration loaded from config.yaml.
        """
        self.fanout = config['target_zone_fanout']
        self.start_offset = config['target_zone_start_offset']
        self.duration = config['target_zone_duration']

    def peaks_to_fingerprints(self, peaks: np.ndarray) -> List[Tuple[str, int]]:
        """
        将峰值点数组转换为指纹列表。
        Converts an array of peaks into a list of fingerprints.

        Args:
            peaks (np.ndarray): 一个 N x 2 的数组，每行代表一个峰值点 (时间, 频率)。
                                An N x 2 array where each row is a peak (time, frequency).

        Returns:
            List[Tuple[str, int]]: 指纹列表，每个元素为 (哈希值, 锚点时间偏移量_毫秒)。
                                   A list of fingerprints, where each element is (hash_string, anchor_time_offset_ms).
        """
        fingerprints = []

        # 确保峰值按时间排序
        peaks = peaks[np.argsort(peaks[:, 0])]

        num_peaks = len(peaks)

        # 遍历每个峰值点作为锚点
        for i in range(num_peaks):
            anchor_time, anchor_freq = peaks[i]

            # 定义目标区域的起始和结束时间
            target_start_time = anchor_time + self.start_offset
            target_end_time = target_start_time + self.duration

            # 在目标区域内寻找配对点
            j = i + 1
            targets_found = 0

            while j < num_peaks and targets_found < self.fanout:
                target_time, target_freq = peaks[j]

                # 如果目标点的时间超出了目标区域，则停止为当前锚点寻找
                if target_time > target_end_time:
                    break

                # 如果目标点在目标区域内，则生成一个哈希
                if target_time >= target_start_time:
                    delta_t = target_time - anchor_time

                    # 生成哈希
                    h = self._generate_hash(anchor_freq, target_freq, delta_t)

                    # 存储 (哈希值, 锚点时间)
                    # 时间存储为整数毫秒，以匹配数据库中的格式
                    fingerprints.append((h, int(anchor_time * 1000)))

                    targets_found += 1

                j += 1

        return fingerprints

    @staticmethod
    def _generate_hash(f1: float, f2: float, dt: float) -> str:
        """
        根据两个频率和一个时间差生成一个SHA1哈希。
        通过分桶来提高对微小变化的鲁棒性。

        Generates a SHA1 hash from two frequencies and a time delta,
        using binning to improve robustness against minor variations.
        """
        # --- 修复：使用分桶来量化输入值 ---
        # 通过除以 FUZ_FACTOR 来对频率和时间差进行分桶，
        # 使得微小的变化不会影响最终的哈希结果。
        # --- FIX: Use binning to quantize input values ---
        # Binning frequencies and the time delta by dividing by FUZ_FACTOR
        # ensures that minor variations do not affect the final hash.
        f1_binned = int(f1 / FUZ_FACTOR)
        f2_binned = int(f2 / FUZ_FACTOR)
        dt_binned = int(dt / FUZ_FACTOR)

        hash_input = f"{f1_binned}|{f2_binned}|{dt_binned}".encode('utf-8')
        return hashlib.sha1(hash_input).hexdigest()
